home *** CD-ROM | disk | FTP | other *** search
- Frequently Asked Questions (FAQS);faqs.222
-
-
-
- p = (char *)((int *)p + 1);
-
- , or simply
-
- p += sizeof(int);
-
- References: ANSI Sec. 3.3.4, Rationale Sec. 3.3.2.4 p. 43.
-
-
- Section 3. Memory Allocation
-
- 3.1: Why doesn't this fragment work?
-
- char *answer;
- printf("Type something:\n");
- gets(answer);
- printf("You typed \"%s\"\n", answer);
-
- A: The pointer variable "answer," which is handed to the gets
- function as the location into which the response should be
- stored, has not been set to point to any valid storage. That
- is, we cannot say where the pointer "answer" points. (Since
- local variables are not initialized, and typically contain
- garbage, it is not even guaranteed that "answer" starts out as a
- null pointer. See question 17.1.)
-
- The simplest way to correct the question-asking program is to
- use a local array, instead of a pointer, and let the compiler
- worry about allocation:
-
- #include <string.h>
-
- char answer[100], *p;
- printf("Type something:\n");
- fgets(answer, 100, stdin);
- if((p = strchr(answer, '\n')) != NULL)
- *p = '\0';
- printf("You typed \"%s\"\n", answer);
-
- Note that this example also uses fgets instead of gets (always a
- good idea), so that the size of the array can be specified, so
- that fgets will not overwrite the end of the array if the user
- types an overly-long line. (Unfortunately for this example,
- fgets does not automatically delete the trailing \n, as gets
- would.) It would also be possible to use malloc to allocate the
- answer buffer, and/or to parameterize its size
- (#define ANSWERSIZE 100).
-
- 3.2: I can't get strcat to work. I tried
-
- char *s1 = "Hello, ";
- char *s2 = "world!";
- char *s3 = strcat(s1, s2);
-
- but I got strange results.
-
- A: Again, the problem is that space for the concatenated result is
- not properly allocated. C does not provide an automatically-
- managed string type. C compilers only allocate memory for
- objects explicitly mentioned in the source code (in the case of
- "strings," this includes character arrays and string literals).
- The programmer must arrange (explicitly) for sufficient space
- for the results of run-time operations such as string
- concatenation, typically by declaring arrays, or by calling
- malloc.
-
- strcat performs no allocation; the second string is appended to
- the first one, in place. Therefore, one fix would be to declare
- the first string as an array with sufficient space:
-
- char s1[20] = "Hello, ";
-
- Since strcat returns the value of its first argument (s1, in
- this case), the s3 variable is superfluous.
-
- Reference: CT&P Sec. 3.2 p. 32.
-
- 3.3: But the man page for strcat says that it takes two char *'s as
- arguments. How am I supposed to know to allocate things?
-
- A: In general, when using pointers you _always_ have to consider
- memory allocation, at least to make sure that the compiler is
- doing it for you. If a library routine's documentation does not
- explicitly mention allocation, it is usually the caller's
- problem.
-
- The Synopsis section at the top of a Unix-style man page can be
- misleading. The code fragments presented there are closer to
- the function definition used by the call's implementor than the
- invocation used by the caller. In particular, many routines
- which accept pointers (e.g. to structs or strings), are usually
- called with the address of some object (a struct, or an array --
- see questions 2.3 and 2.4.) Another common example is stat().
-
- 3.4: I have a function that is supposed to return a string, but when
- it returns to its caller, the returned string is garbage.
-
- A: Make sure that the memory to which the function returns a
- pointer is correctly allocated. The returned pointer should be
- to a statically-allocated buffer, or to a buffer passed in by
- the caller, but _not_ to a local array. See also question 17.3.
-
- 3.5: You can't use dynamically-allocated memory after you free it,
- can you?
-
- A: No. Some early man pages for malloc stated that the contents of
- freed memory was "left undisturbed;" this ill-advised guarantee
- was never universal and is not required by ANSI.
-
- Few programmers would use the contents of freed memory
- deliberately, but it is easy to do so accidentally. Consider
- the following (correct) code for freeing a singly-linked list:
-
- struct list *listp, *nextp;
- for(listp = base; listp != NULL; listp = nextp) {
- nextp = listp->next;
- free((char *)listp);
- }
-
- and notice what would happen if the more-obvious loop iteration
- expression listp = listp->next were used, without the temporary
- nextp pointer.
-
- References: ANSI Rationale Sec. 4.10.3.2 p. 102; CT&P Sec. 7.10
- p. 95.
-
- 3.6: How does free() know how many bytes to free?
-
- A: The malloc/free package remembers the size of each block it
- allocates and returns, so it is not necessary to remind it of
- the size when freeing.
-
- 3.7: So can I query the malloc package to find out how big an
- allocated block is?
-
- A: Not portably.
-
- 3.8: Is it legal to pass a null pointer as the first argument to
- realloc()? Why would you want to?
-
- A: ANSI C sanctions this usage (and the related realloc(..., 0),
- which frees), but several earlier implementations do not support
- it, so it is not widely portable. Passing an initially-null
- pointer to realloc can make it easier to write a self-starting
- incremental allocation algorithm.
-
- References: ANSI Sec. 4.10.3.4 .
-
- 3.9: What is the difference between calloc and malloc? Is it safe to
- use calloc's zero-fill guarantee for pointer and floating-point
- values? Does free work on memory allocated with calloc, or do
- you need a cfree?
-
- A: calloc(m, n) is essentially equivalent to
-
- p = malloc(m * n);
- memset(p, 0, m * n);
-
- The zero fill is all-bits-zero, and does not therefore guarantee
- useful zero values for pointers (see section 1 of this list) or
- floating-point values. free can (and should) be used to free
- the memory allocated by calloc.
-
- References: ANSI Secs. 4.10.3 to 4.10.3.2 .
-
- 3.10: What is alloca and why is its use discouraged?
-
- A: alloca allocates memory which is automatically freed when the
- function which called alloca returns. That is, memory allocated
- with alloca is local to a particular function's "stack frame" or
- context.
-
- alloca cannot be written portably, and is difficult to implement
- on machines without a stack. Its use is problematical (and the
- obvious implementation on a stack-based machine fails) when its
- return value is passed directly to another function, as in
- fgets(alloca(100), 100, stdin).
-
- For these reasons, alloca cannot be used in programs which must
- be widely portable, no matter how useful it might be.
-
- References: ANSI Rationale Sec. 4.10.3 p. 102.
-
-
- Section 4. Expressions
-
- 4.1: Why doesn't this code:
-
- a[i] = i++;
-
- work?
-
- A: The subexpression i++ causes a side effect -- it modifies i's
- value -- which leads to undefined behavior if i is also
- referenced elsewhere in the same expression.
-
- References: ANSI Sec. 3.3 p. 39.
-
- 4.2: Under my compiler, the code
-
- int i = 7;
- printf("%d\n", i++ * i++);
-
- prints 49. Regardless of the order of evaluation, shouldn't it
- print 56?
-
- A: Although the postincrement and postdecrement operators ++ and --
- perform the operations after yielding the former value, the
- implication of "after" is often misunderstood. It is _not_
- guaranteed that the operation is performed immediately after
- giving up the previous value and before any other part of the
- expression is evaluated. It is merely guaranteed that the
- update will be performed sometime before the expression is
- considered "finished" (before the next "sequence point," in ANSI
- C's terminology). In the example, the compiler chose to
- multiply the previous value by itself and to perform both
- increments afterwards.
-
- The behavior of code which contains multiple, ambiguous side
- effects has always been undefined. Don't even try to find out
- how your compiler implements such things (contrary to the ill-
- advised exercises in many C textbooks); as K&R wisely point out,
- "if you don't know _how_ they are done on various machines, that
- innocence may help to protect you."
-
- References: K&R I Sec. 2.12 p. 50; K&R II Sec. 2.12 p. 54; ANSI
- Sec. 3.3 p. 39; CT&P Sec. 3.7 p. 47; PCS Sec. 9.5 pp. 120-1.
- (Ignore H&S Sec. 7.12 pp. 190-1, which is obsolete.)
-
- 4.3: But what about the &&, ||, and comma operators?
- I see code like "if((c = getchar()) == EOF || c == '\n')" ...
-
- A: There is a special exception for those operators, (as well as
- ?: ); each of them does imply a sequence point (i.e. left-to-
- right evaluation is guaranteed). Any book on C should make this
- clear.
-
- References: K&R I Sec. 2.6 p. 38, Secs. A7.11-12 pp. 190-1;
- K&R II Sec. 2.6 p. 41, Secs. A7.14-15 pp. 207-8; ANSI
- Secs. 3.3.13 p. 52, 3.3.14 p. 52, 3.3.15 p. 53, 3.3.17 p. 55,
- CT&P Sec. 3.7 pp. 46-7.
-
- 4.4: If I'm not using the value of the expression, should I use i++
- or ++i to increment a variable?
-
- A: Since the two forms differ only in the value yielded, they are
- entirely equivalent when only their side effect is needed. Some
- people will tell you that in the old days one form was preferred
- over the other because it utilized a PDP-11 autoincrement
- addressing mode, but those people are confused.
-
- 4.5: Why doesn't the code
-
- int a = 1000, b = 1000;
- long int c = a * b;
-
- work?
-
- A: Under C's integral promotion rules, the multiplication is
- carried out using int arithmetic, and the result may overflow
- and/or be truncated before being assigned to the long int left-
- hand-side. Use an explicit cast to force long arithmetic:
-
- long int c = (long int)a * b;
-
-
- Section 5. ANSI C
-
- 5.1: What is the "ANSI C Standard?"
-
- A: In 1983, the American National Standards Institute commissioned
- a committee, X3J11, to standardize the C language. After a
- long, arduous process, including several widespread public
- reviews, the committee's work was finally ratified as an
- American National Standard, X3.159-1989, on December 14, 1989,
- and published in the spring of 1990. For the most part, ANSI C
- standardizes existing practice, with a few additions from C++
- (most notably function prototypes) and support for multinational
- character sets (including the much-lambasted trigraph
- sequences). The ANSI C standard also formalizes the C run-time
- library support routines.
-
- The published Standard includes a "Rationale," which explains
- many of its decisions, and discusses a number of subtle points,
- including several of those covered here. (The Rationale is "not
- part of ANSI Standard X3.159-1989, but is included for
- information only.")
-
- The Standard has been adopted as an international standard,
- ISO/IEC 9899:1990, although the sections are numbered
- differently (briefly, ANSI sections 2 through 4 correspond
- roughly to ISO sections 5 through 7), and the Rationale is
- currently not included.
-
- 5.2: How can I get a copy of the Standard?
-
- A: Copies are available from
-
- American National Standards Institute
- 11 W. 42nd St., 13th floor
- New York, NY 10036 USA
- (+1) 212 642 4900
-
- or
-
- Global Engineering Documents
- 2805 McGaw Avenue
- Irvine, CA 92714 USA
- (+1) 714 261 1455
- (800) 854 7179 (U.S. & Canada)
-
- The cost from ANSI is $50.00, plus $6.00 shipping. Quantity
- discounts are available. (Note that ANSI derives revenues to
- support its operations from the sale of printed standards, so
- electronic copies are _not_ available.)
-
- The Rationale, by itself, has been printed by Silicon Press,
- ISBN 0-929306-07-4.
-
- 5.3: Does anyone have a tool for converting old-style C programs to
- ANSI C, or vice versa, or for automatically generating
- prototypes?
-
- A: Two programs, protoize and unprotoize, convert back and forth
- between prototyped and "old style" function definitions and
- declarations. (These programs do _not_ handle full-blown
- translation between "Classic" C and ANSI C.) These programs
- exist as patches to the FSF GNU C compiler, gcc. Look for the
- file protoize-1.39.0.5.Z in pub/gnu at prep.ai.mit.edu
- (18.71.0.38), or at several other FSF archive sites.
-
- The unproto program (/pub/unix/unproto4.shar.Z on
- ftp.win.tue.nl) is a filter which sits between the preprocessor
- and the next compiler pass, converting most of ANSI C to
- traditional C on-the-fly.
-
- The GNU GhostScript package comes with a little program called
- ansi2knr.
-
- Several prototype generators exist, many as modifications to
- lint. Version 3 of CPROTO was posted to comp.sources.misc in
- March, 1992. (See also question 17.8.)
-
- 5.4: I'm trying to use the ANSI "stringizing" preprocessing operator
- # to insert the value of a symbolic constant into a message, but
- it keeps stringizing the macro's name rather than its value.
-
- A: You must use something like the following two-step procedure to
- force the macro to be expanded as well as stringized:
-
- #define str(x) #x
- #define xstr(x) str(x)
- #define OP plus
- char *opname = xstr(OP);
-
- This sets opname to "plus" rather than "OP".
-
- An equivalent circumlocution is necessary with the token-pasting
- operator ## when the values (rather than the names) of two
- macros are to be concatenated.
-
- References: ANSI Sec. 3.8.3.2, Sec. 3.8.3.5 example p. 93.
-
- 5.5: What's the difference between "char const *p" and
- "char * const p"?
-
- A: "char const *p" is a pointer to a constant character (you can't
- change the character); "char * const p" is a constant pointer to
- a (variable) character (i.e. you can't change the pointer).
- (Read these "inside out" to understand them. See question
- 10.3.)
-
- References: ANSI Sec. 3.5.4.1 .
-
- 5.6: My ANSI compiler complains about a mismatch when it sees
-
- extern int func(float);
-
- int func(x)
- float x;
- {...
-
- A: You have mixed the new-style prototype declaration
- "extern int func(float);" with the old-style definition
- "int func(x) float x;". Old C (and ANSI C, in the absence of
- prototypes, and in variable-length argument lists) "widens"
- certain arguments when they are passed to functions. floats
- are promoted to double, and characters and short integers are
- promoted to integers. (The values are automatically coerced
- back to the corresponding narrower types within the body of the
- called function, if they are declared that way there.)
-
- The problem can be fixed either by using new-style syntax
- consistently in the definition:
-
- int func(float x) { ... }
-
- or by changing the new-style prototype declaration to match the
- old-style definition:
-
- extern int func(double);
-
- (In this case, it would be clearest to change the old-style
- definition to use double as well, as long as the address of that
- parameter is not taken.)
-
- It may also be safer to avoid "narrow" (char, short int, and
- float) function arguments and return types.
-
- References: ANSI Sec. 3.3.2.2 .
-
- 5.7: I'm getting strange syntax errors inside code which I've
- #ifdeffed out.
-
- A: Under ANSI C, the text inside a "turned off" #if, #ifdef, or
- #ifndef must still consist of "valid preprocessing tokens."
- This means that there must be no unterminated comments or quotes
- (note particularly that an apostrophe within a contracted word
- could look like the beginning of a character constant), and no
- newlines inside quotes. Therefore, natural-language comments
- and pseudocode should always be written between the "official"
- comment delimiters /* and */. (But see also question 17.10, and
- 6.7.)
-
- References: ANSI Sec. 2.1.1.2 p. 6, Sec. 3.1 p. 19 line 37.
-
- 5.8: Can I declare main as void, to shut off these annoying "main
- returns no value" messages? (I'm calling exit(), so main
- doesn't return.)
-
- A: No. main must be declared as returning an int, and as taking
- either zero or two arguments (of the appropriate type). If
- you're calling exit() but still getting warnings, you'll have to
- insert a redundant return statement (or use some kind of
- "notreached" directive, if available).
-
- References: ANSI Sec. 2.1.2.2.1 pp. 7-8.
-
- 5.9: Why does the ANSI Standard not guarantee more than six monocase
- characters of external identifier significance?
-
- A: The problem is older linkers which are neither under the control
- of the ANSI standard nor the C compiler developers on the
- systems which have them. The limitation is only that
- identifiers be _significant_ in the first six characters, not
- that they be restricted to six characters in length. This
- limitation is annoying, but certainly not unbearable, and is
- marked in the Standard as "obsolescent," i.e. a future revision
- will likely relax it.
-
- This concession to current, restrictive linkers really had to be
- made, no matter how vehemently some people oppose it. (The
- Rationale notes that its retention was "most painful.") If you
- disagree, or have thought of a trick by which a compiler
- burdened with a restrictive linker could present the C
- programmer with the appearance of more significance in external
- identifiers, read the excellently-worded section 3.1.2 in the
- X3.159 Rationale (see question 5.1), which discusses several
- such schemes and explains why they could not be mandated.
-
- References: ANSI Sec. 3.1.2 p. 21, Sec. 3.9.1 p. 96, Rationale
- Sec. 3.1.2 pp. 19-21.
-
- 5.10: What is the difference between memcpy and memmove?
-
- A: memmove offers guaranteed behavior if the source and destination
- arguments overlap. memcpy makes no such guarantee, and may
- therefore be more efficiently implementable. When in doubt,
- it's safer to use memmove.
-
- References: ANSI Secs. 4.11.2.1, 4.11.2.2, Rationale
- Sec. 4.11.2 .
-
- 5.11: My compiler is rejecting the simplest possible test programs,
- with all kinds of syntax errors.
-
- A: Perhaps it is a pre-ANSI compiler, unable to accept function
- prototypes and the like.
-
- 5.12: Why won't the Frobozz Magic C Compiler, which claims to be ANSI
- compliant, accept this code? I know that the code is ANSI,
- because gcc accepts it.
-
- A: Most compilers support a few non-Standard extensions, gcc more
- so than most. Are you sure that the code being rejected doesn't
- rely on such an extension? It is usually a bad idea to perform
- experiments with a particular compiler to determine properties
- of a language; the applicable standard may permit variations, or
- the compiler may be wrong.
-
- 5.13: What are #pragmas and what are they good for?
-
- A: The #pragma directive provides a single, well-defined "escape
- hatch" which can be used for all sorts of implementation-
- specific controls and extensions: source listing control,
- structure packing, warning suppression (like the old lint
- /* NOTREACHED */ comments), etc.
-
- References: ANSI Sec. 3.8.6 .
-
-
- Section 6. C Preprocessor
-
- 6.1: How can I write a generic macro to swap two values?
-
- A: There is no good answer to this question. If the values are
- integers, a well-known trick using exclusive-OR could perhaps be
- used, but it will not work for floating-point values or
- pointers, or if the two values are the same variable (and the
- "obvious" supercompressed implementation for integral types
- a^=b^=a^=b is in fact illegal due to multiple side-effects,
- and...). If the macro is intended to be used on values of
- arbitrary type (the usual goal), it cannot use a temporary,
- since it does not know what type of temporary it needs, and
- standard C does not provide a typeof operator.
-
- The best all-around solution is probably to forget about using a
- macro, unless you're willing to pass in the type as a third
- argument.
-
- 6.2: I have some old code that tries to construct identifiers with a
- macro like
-
- #define Paste(a, b) a/**/b
-
- but it doesn't work any more.
-
- A: That comments disappeared entirely and could therefore be used
- for token pasting was an undocumented feature of some early
- preprocessor implementations, notably Reiser's. ANSI affirms
- (as did K&R) that comments are replaced with white space.
- However, since the need for pasting tokens was demonstrated and
- real, ANSI introduced a well-defined token-pasting operator, ##,
- which can be used like this:
-
- #define Paste(a, b) a##b
-
- (See also question 5.4.)
-
- Reference: ANSI Sec. 3.8.3.3 p. 91, Rationale pp. 66-7.
-
- 6.3: What's the best way to write a multi-statement cpp macro?
-
- A: The usual goal is to write a macro that can be invoked as if it
- were a single function-call statement. This means that the
- "caller" will be supplying the final semicolon, so the macro
- body should not. The macro body cannot be a simple brace-
- delineated compound statement, because syntax errors would
- result if it were invoked (apparently as a single statement, but
- with a resultant extra semicolon) as the if branch of an if/else
- statement with an explicit else clause.
-
- The traditional solution is to use
-
- #define Func() do { \
- /* declarations */ \
- stmt1; \
- stmt2; \
- /* ... */ \
- } while(0) /* (no trailing ; ) */
-
- When the "caller" appends a semicolon, this expansion becomes a
- single statement regardless of context. (An optimizing compiler
- will remove any "dead" tests or branches on the constant
- condition 0, although lint may complain.)
-
- If all of the statements in the intended macro are simple
- expressions, with no declarations or loops, another technique is
- to write a single, parenthesized expression using one or more
- comma operators. (See the example under question 6.8 below.
- This technique also allows a value to be "returned.")
-
- Reference: CT&P Sec. 6.3 pp. 82-3.
-
- 6.4: Is it acceptable for one header file to #include another?
-
- A: There has been considerable debate surrounding this question.
- Many people believe that "nested #include files" are to be
- avoided: the prestigious Indian Hill Style Guide (see question
- 14.3) disparages them; they can make it harder to find relevant
- definitions; they can lead to multiple-declaration errors if a
- file is #included twice; and they make manual Makefile
- maintenance very difficult. On the other hand, they make it
- possible to use header files in a modular way (a header file
- #includes what it needs itself, rather than requiring each
- #includer to do so, a requirement that can lead to intractable
- headaches); a tool like grep (or a tags file) makes it easy to
- find definitions no matter where they are; a popular trick:
-
- #ifndef HEADER_FILE_NAME
- #define HEADER_FILE_NAME
- ...header file contents...
- #endif
-
- makes a header file "idempotent" so that it can safely be
- #included multiple times; and automated Makefile maintenance
- tools (which are a virtual necessity in large projects anyway)
- handle dependency generation in the face of nested #include
- files easily.
-
- 6.5: Does the sizeof operator work in preprocessor #if directives?
-
- A: No. Preprocessing happens during an earlier pass of
- compilation, before type names have been parsed. Consider using
- the predefined constants in ANSI's <limits.h>, or a "configure"
- script, instead.
-
- References: ANSI Sec. 2.1.1.2 pp. 6-7, Sec. 3.8.1 p. 87
- footnote 83.
-
- 6.6: How can I use a preprocessor #if expression to tell if a machine
- is big-endian or little-endian?
-
- A: You probably can't. (Preprocessor arithmetic uses only long
- ints, and there is no concept of addressing.) Are you sure you
- need to know the machine's endianness explicitly? Usually it's
- better to write code which doesn't care.
-
- 6.7: I've got this tricky processing I want to do at compile time and
- I can't figure out a way to get cpp to do it.
-
- A: cpp is not intended as a general-purpose preprocessor. Rather
- than forcing it to do something inappropriate, consider writing
- your own little special-purpose preprocessing tool, instead.
- You can easily get a utility like make(1) to run it for you
- automatically.
-
- If you are trying to preprocess something other than C, consider
- using a general-purpose preprocessor (such as m4).
-
- 6.8: How can I write a cpp macro which takes a variable number of
- arguments?
-
- A: One popular trick is to define the macro with a single argument,
- and call it with a double set of parentheses, which appear to
- the preprocessor to indicate a single argument:
-
- #define DEBUG(args) (printf("DEBUG: "), printf args)
-
- if(n != 0) DEBUG(("n is %d\n", n));
-
- The obvious disadvantage is that the caller must always remember
- to use the extra parentheses. (It is often better to use a
- bona-fide function, which can take a variable number of
- arguments in a well-defined way. See questions 7.1 and 7.2
- below.)
-
-
- Section 7. Variable-Length Argument Lists
-
- 7.1: How can I write a function that takes a variable number of
- arguments?
-
- A: Use the <stdarg.h> header (or, if you must, the older
- <varargs.h>).
-
- Here is a function which concatenates an arbitrary number of
- strings into malloc'ed memory:
-
- #include <stdlib.h> /* for malloc, NULL, size_t */
- #include <stdarg.h> /* for va_ stuff */
- #include <string.h> /* for strcat et al */
-
- char *vstrcat(char *first, ...)
- {
- size_t len = 0;
- char *retbuf;
- va_list argp;
- char *p;
-
- if(first == NULL)
- return NULL;
-
- len = strlen(first);
-
- va_start(argp, first);
-
- while((p = va_arg(argp, char *)) != NULL)
- len += strlen(p);
-
- va_end(argp);
-
- retbuf = malloc(len + 1); /* +1 for trailing \0 */
-
- if(retbuf == NULL)
- return NULL; /* error */
-
- (void)strcpy(retbuf, first);
-
- va_start(argp, first);
-
- while((p = va_arg(argp, char *)) != NULL)
- (void)strcat(retbuf, p);
-
- va_end(argp);
-
- return retbuf;
- }
-
- Usage is something like
-
- char *str = vstrcat("Hello, ", "world!", (char *)NULL);
-
- Note the cast on the last argument. (Also note that the caller
- must free the returned, malloc'ed storage.)
-
- Under a pre-ANSI compiler, rewrite the function definition
- without a prototype ("char *vstrcat(first) char *first; {"),
- include <stdio.h> rather than <stdlib.h>, add "extern
- char *malloc();", and use int instead of size_t. You may also
- have to delete the (void) casts, and use the older varargs
- package instead of stdarg. See the next question for hints.
-
- Remember that in variable-length argument lists, function
- prototypes do not supply parameter type information; therefore,
- default argument promotions apply (see question 5.6), and null
- pointer arguments must be typed explicitly (see question 1.2).
-
- References: K&R II Sec. 7.3 p. 155, Sec. B7 p. 254; H&S
- Sec. 13.4 pp. 286-9; ANSI Secs. 4.8 through 4.8.1.3 .
-
- 7.2: How can I write a function that takes a format string and a
- variable number of arguments, like printf, and passes them to
- printf to do most of the work?
-
- A: Use vprintf, vfprintf, or vsprintf.
-
- Here is an "error" routine which prints an error message,
- preceded by the string "error: " and terminated with a newline:
-
- #include <stdio.h>
- #include <stdarg.h>
-
- void
- error(char *fmt, ...)
- {
- va_list argp;
- fprintf(stderr, "error: ");
- va_start(argp, fmt);
- vfprintf(stderr, fmt, argp);
- va_end(argp);
- fprintf(stderr, "\n");
- }
-
- To use the older <varargs.h> package, instead of <stdarg.h>,
- change the function header to:
-
- void error(va_alist)
- va_dcl
- {
- char *fmt;
-
- change the va_start line to
-
- va_start(argp);
-
- and add the line
-
- fmt = va_arg(argp, char *);
-
- between the calls to va_start and vfprintf. (Note that there is
- no semicolon after va_dcl.)
-
- References: K&R II Sec. 8.3 p. 174, Sec. B1.2 p. 245; H&S
- Sec. 17.12 p. 337; ANSI Secs. 4.9.6.7, 4.9.6.8, 4.9.6.9 .
-
- 7.3: How can I discover how many arguments a function was actually
- called with?
-
- A: This information is not available to a portable program. Some
- systems provide a nonstandard nargs() function, but its use is
- questionable, since it typically returns the number of words
- passed, not the number of arguments. (Floating point values and
- structures are usually passed as several words.)
-
- Any function which takes a variable number of arguments must be
- able to determine from the arguments themselves how many of them
- there are. printf-like functions do this by looking for
- formatting specifiers (%d and the like) in the format string
- (which is why these functions fail badly if the format string
- does not match the argument list). Another common technique
- (useful when the arguments are all of the same type) is to use a
- sentinel value (often 0, -1, or an appropriately-cast null
- pointer) at the end of the list (see the execl and vstrcat
- examples under questions 1.2 and 7.1 above).
-
- 7.4: How can I write a function which takes a variable number of
- arguments and passes them to some other function (which takes a
- variable number of arguments)?
-